// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 *
 * Description: Euler Net Boost selftest.
 */
#include <test_progs.h>

#include "enboost_kern.skel.h"
#include "network_helpers.h"
#include "cgroup_helpers.h"

#define IFINDEX_LO	1
#define MSG_LEN		8
#define BUF_LEN		10
#define CGRP_PATH	"/cgrp_enboost"

static int client_fd = -1;
static int listen_fd = -1;
static int service_fd = -1;

static int start_client_and_server(struct enboost_kern *skel)
{
	int ret;
	struct sockaddr_in addr;
	socklen_t addrlen = sizeof(addr);

	listen_fd = start_server(AF_INET, SOCK_STREAM, NULL, 0, 0);
	if (!ASSERT_GE(listen_fd, 0, "listen_fd"))
		return -1;

	client_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (!ASSERT_GE(client_fd, 0, "client_fd"))
		return -1;

	ret = getsockname(listen_fd, (struct sockaddr *)&addr, &addrlen);
	if (!ASSERT_GE(ret, 0, "getsockname"))
		return -1;

	skel->bss->g_serv_port = ntohs(addr.sin_port);

	ret = connect_fd_to_fd(client_fd, listen_fd, 0);
	if (!ASSERT_OK(ret, "connect_fd_to_fd"))
		return -1;

	service_fd = accept(listen_fd, NULL, NULL);
	if (!ASSERT_GE(service_fd, 0, "service_fd"))
		return -1;

	return 0;
}

static int do_enboost_test(struct enboost_kern *skel)
{
	char msg[MSG_LEN] = "enboost";
	char buf[BUF_LEN];
	int cnt;

	/* Scenario 1: server send msg to client */
	skel->bss->rcv_msg_len = sizeof(msg);
	skel->bss->is_rcv_boost = 0;

	cnt = write(service_fd, msg, sizeof(msg));
	if (!ASSERT_EQ(cnt, sizeof(msg), "server write"))
		return -1;

	cnt = read(client_fd, buf, sizeof(buf));
	if (!ASSERT_EQ(cnt, sizeof(msg), "client read"))
		return -1;

	if (!ASSERT_STREQ(buf, msg, "client rcv check"))
		return -1;

	if (!ASSERT_EQ(skel->bss->is_rcv_boost, 1, "client rcv boost"))
		return -1;

	/* Scenario 2: client send msg to server */
	skel->bss->snd_msg_len = sizeof(msg);
	skel->bss->is_snd_boost = 0;

	cnt = write(client_fd, msg, sizeof(msg));
	if (!ASSERT_EQ(cnt, sizeof(msg), "client write"))
		return -1;

	cnt = read(service_fd, buf, sizeof(buf));
	if (!ASSERT_EQ(cnt, sizeof(msg), "server read"))
		return -1;

	if (!ASSERT_STREQ(buf, msg, "server rcv check"))
		return -1;

	if (!ASSERT_EQ(skel->bss->is_snd_boost, 1, "client snd boost"))
		return -1;

	return 0;
}

void test_enboost(void)
{
	struct bpf_link *ingress_link = NULL;
	struct bpf_link *egress_link = NULL;
	struct enboost_kern *skel;
	int cgrp_fd = -1;
	int ret;

	skel = enboost_kern__open_and_load();
	if (!ASSERT_OK(!skel, "skel_open_load"))
		return;

	cgrp_fd = cgroup_setup_and_join(CGRP_PATH);
	if (!ASSERT_GE(cgrp_fd, 0, "cgrp_fd"))
		goto cleanup;

	ret = start_client_and_server(skel);
	if (!ASSERT_GE(ret, 0, "start_client_and_server"))
		goto cleanup;

	ingress_link = bpf_program__attach_xdp(skel->progs.enboost_ingress_prog, IFINDEX_LO);
	if (!ASSERT_OK_PTR(ingress_link, "ingress_link"))
		goto cleanup;

	egress_link = bpf_program__attach_cgroup(skel->progs.enboost_egress_prog, cgrp_fd);
	if (!ASSERT_OK_PTR(egress_link, "egress_link"))
		goto cleanup;

	ret = do_enboost_test(skel);
	if (!ASSERT_GE(ret, 0, "do_enboost_test"))
		goto cleanup;
cleanup:
	bpf_link__destroy(egress_link);
	bpf_link__destroy(ingress_link);
	close(client_fd);
	close(service_fd);
	close(listen_fd);
	close(cgrp_fd);
	cleanup_cgroup_environment();
	enboost_kern__destroy(skel);
}
